package DistSys;

import java.io.*;
import javax.swing.UIManager;
import java.util.*;

/**
 * The class <code>PersonalInfo</code> stores all information that are personal
 * to the user or the users computer.
 *
 */

public class PersonalInfo {

    public static final int PATH_PROJECTS = 0;
    public static final int PATH_TEMP     = 1;
    public static final int MAX_PATH      = 2;
    private File[] afPaths;

    //external files
    public static final int EXT_FILE_EXCEL    = 0;
    public static final int MAX_EXT_FILE      = 1;
    private File[] afExtFiles;

    public static final int MAX_RECENT_PRJ = 5;
    private File[] afRecentPrjs;

    private String sLookAndFeel;

    private static transient File fGlobalDefaultTemplateFile = null;
    private LinkedList lTempFileList;

    /**
     * Constructs a <code>PersonalInfo</code> object and initializes it with
     * hopefully resonable default values.
     */
    public PersonalInfo() {
        // just use the init method since everything is preinitialized to null
        init();
    }


    /**
     * Initializes variables, which are not initialized yet, with hopefully
     * resonable default values.
     */
    private void init() {
        // Check afPaths
        if (afPaths == null) {
            // afPaths does not exist, create it
            afPaths = new File[MAX_PATH];
        } else if (afPaths.length < MAX_PATH) {
            // afPaths is too small, enlarge it
            File[] afNewPaths = new File[MAX_PATH];
            System.arraycopy(afPaths, 0, afNewPaths, 0, afPaths.length);
            afPaths = afNewPaths;
        }
        if (afPaths[PATH_PROJECTS] == null || afPaths[PATH_TEMP] == null) {
            // The projects or the temp path does not exist, ...
            File fRoot;
            if (System.getProperty("os.name").toLowerCase().indexOf("windows") >= 0) {
                // ... at Windows try the old pad path first
                fRoot = new File("C:\\PAD");
                if (!fRoot.exists()) {
                    // The old pad path was not found so choose $ROOT/iPad
                    fRoot = new File(System.getProperty("user.home"));
                    File fParent;
                    while ((fParent = fRoot.getParentFile()) != null)
                        fRoot = fParent;
                    fRoot = new File(fRoot, "iPad");
                }
            } else
                // ... at any other os choos $HOME/iPad
                fRoot = new File(System.getProperty("user.home"), "iPad");

            if (afPaths[PATH_PROJECTS] == null) {
                afPaths[PATH_PROJECTS] = new File(fRoot, "Projects");
            }
            if (afPaths[PATH_TEMP] == null) {
                afPaths[PATH_TEMP] = new File(fRoot, "Temp");
            }
        }

        // Check afExtFiles
        if (afExtFiles == null) {
            // afExtFiles does not exist, create it
            afExtFiles = new File[MAX_EXT_FILE];
        } else if (afExtFiles.length < MAX_EXT_FILE) {
            // afExtFiles is too small, enlarge it
            File[] afNewFiles = new File[MAX_EXT_FILE];
            System.arraycopy(afExtFiles, 0, afNewFiles, 0, afExtFiles.length);
            afExtFiles = afNewFiles;
        }
        if (afExtFiles[EXT_FILE_EXCEL] == null) {
            // The excel file does not exist, ...
            if (System.getProperty("os.name").toLowerCase().indexOf("windows") >= 0) {
                // ... at Windows try the standard path first
                afExtFiles[EXT_FILE_EXCEL] = new File("C:\\Program Files\\Microsoft " +
                                               "Office\\Office\\Excel.exe");
                if (!afExtFiles[EXT_FILE_EXCEL].exists()) {
                    // The standard installation path does not work, use blank file
                    afExtFiles[EXT_FILE_EXCEL] = new File("");
                }
            } else
                // ... at any other os choose nothing.
                afExtFiles[EXT_FILE_EXCEL] = new File("");
        }

        // Check afRecentPrjs
        if (afRecentPrjs == null) {
            afRecentPrjs = new File[MAX_RECENT_PRJ];
        } else if (afRecentPrjs.length < MAX_RECENT_PRJ) {
            // afRecentPrjs is too small, enlarge it
            File[] afNewRecentPrj = new File[MAX_RECENT_PRJ];
            System.arraycopy(afRecentPrjs, 0, afNewRecentPrj, 0,
                afRecentPrjs.length);
            afRecentPrjs = afNewRecentPrj;
        }

        // Check sLookAndFeel
        if (sLookAndFeel == null) {
            // Use the systems default look and feel
            sLookAndFeel = UIManager.getSystemLookAndFeelClassName();
        }

        if (lTempFileList == null)
            lTempFileList = new LinkedList();
        else if (lTempFileList.size() > 0)
            deleteTempFiles();
    }

    /**
     * Returns the path with the given index.
     *
     * @param index  a <code>int</code> specifying which path should be
     *               returned.
     *
     * @return  the path with the given index
     *
     * @see #setPath
     * @see #getProjectsPath
     * @see #getTempPath
     */
    public final File getPath(int index) { return afPaths[index]; }

    /**
     * Returns the path of the projects folder.
     *
     * <p>This method is identical to
     * <code>getPath(PersonalInfo.PATH_PROJECTS)</code>.
     *
     * @return  the path of the projects folder
     *
     * @see #getPath
     * @see #setPath
     * @see #getTempPath
     */
    public final File getProjectsPath() { return afPaths[PATH_PROJECTS]; }

    /**
     * Returns the path that should be used for temporary files.
     *
     * <p>This method is identical to
     * <code>getPath(PersonalInfo.PATH_TEMP)</code>.
     *
     * @return  the path for temporary files
     *
     * @see #getPath
     * @see #setPath
     * @see #getProjectsPath
     */
    public File getTempPath() {
        if (!afPaths[PATH_TEMP].exists())
            afPaths[PATH_TEMP].mkdirs();
        return afPaths[PATH_TEMP];
    }

    /**
     * Set the path with the given index
     *
     * @param index  a <code>int</code> specifying which path should be set.
     * @param path   a <code>File</code> specifying the new folder.
     *
     * @see #getPath
     * @see #getProjectsPath
     * @see #getTempPath
     */
    public final void setPath(int index, File path) { afPaths[index] = path; }

    /**
     * Returns the file with the given index.
     *
     * @param index  a <code>int</code> specifying which file should be
     *               returned.
     *
     * @return  the filewith the given index
     *
     * @see #setExtFile
     * @see #execFile
     */
    public final File getExtFile(int index) { return afExtFiles[index]; }

    /**
     * Set the file with the given index
     *
     * @param index  a <code>int</code> specifying which file should be set.
     * @param file   a <code>File</code> specifying the new file.
     *
     * @see #getExtFile
     * @see #execFile
     */
    public final void setExtFile(int index, File file) { afExtFiles[index] = file; }

    /**
     * Executes the file with the given index with the given argument, if the
     * file exists.
     *
     * @param index     a <code>int</code> specifying which file should be
     *                  executed.
     * @param argument  a string containing the argument.
     *
     * @return  a <code>Process</code> object for managing the subprocess,
     *          or null if the file is not existing.
     * @exception  IOException if an I/O error occurs.
     *
     * @see java.lang.Runtime.exec
     */
    public Process execFile(int index, String argument) throws IOException {
        if (afExtFiles[index] != null && afExtFiles[index].exists()) {
            String[] cmdarray = { afExtFiles[index].getPath(), argument };
            return Runtime.getRuntime().exec(cmdarray, null, null);
        } else
            return null;
    }

    /**
     * Executes the file with the given index with the given arguments, if the
     * file exists.
     *
     * @param index      a <code>int</code> specifying which file should be
     *                   executed.
     * @param arguments  a array of strings containing the arguments.
     *
     * @return  a <code>Process</code> object for managing the subprocess,
     *          or null if the file is not existing.
     * @exception  IOException if an I/O error occurs.
     *
     * @see java.lang.Runtime.exec
     */
    public Process execFile(int index, String[] arguments) throws IOException {
        if (afExtFiles[index] != null && afExtFiles[index].exists()) {
            String[] cmdarray = new String[arguments.length+1];
            cmdarray[0] = afExtFiles[index].getPath();
            System.arraycopy(arguments, 0, cmdarray, 1, arguments.length);
            return Runtime.getRuntime().exec(cmdarray, null, null);
        } else
            return null;
    }

    /**
     * Executes Microsoft Excel with the given argument, if its filename is
     * known and the file is existing.
     *
     * @param argument  a string containing the argument.
     *
     * @return  a <code>Process</code> object for managing the subprocess,
     *          or null if the filename of Microsoft Excel is unknown.
     * @exception  IOException if an I/O error occurs.
     *
     * @see java.lang.Runtime.exec
     */
    public final Process execExcel(String argument) throws IOException {
        return execFile(EXT_FILE_EXCEL, argument);
    }

    /**
     * Executes Microsoft Excel with the given arguments, if its filename is
     * known and the file is existing.
     *
     * @param arguments  a array of strings containing the arguments.
     *
     * @return  a <code>Process</code> object for managing the subprocess,
     *          or null if the filename of Microsoft Excel is unknown.
     * @exception  IOException if an I/O error occurs.
     *
     * @see java.lang.Runtime.exec
     */
    public Process execExcel(String[] arguments) throws IOException {
        return execFile(EXT_FILE_EXCEL, arguments);
    }

    /**
     * Returns the recent project with the given index.
     *
     * @param index  a integer denoting the index
     *
     * @return  the <code>File</code> denoting the recent project
     *
     * @see #addRecentProject
     */
    public final File getRecentProject(int index) { return afRecentPrjs[index];}

    /**
     * Adds the given file to the recent project list. This may change the
     * order in the list.
     *
     * @param f  a <code>File</code> denoting the recent project to add.
     *
     * @see #getLookAndFeelClassName
     */
    public final void addRecentProject(File f) {
        if (f.equals(afRecentPrjs[0])) {
            // Nothing to do if the file is already at the first place
            return;
        }
        for (int i = 1; i < MAX_RECENT_PRJ && afRecentPrjs[i] != null; i++) {
            if (f.equals(afRecentPrjs[i])) {
                // File is found. Move everything up to this place by one and
                // place the file at the beginning.
                System.arraycopy(afRecentPrjs, 0, afRecentPrjs, 1, i);
                afRecentPrjs[0] = f;
                return;
            }
        }
        // File was not found. Move everything by one and place the file at the
        // beginning.
        System.arraycopy(afRecentPrjs, 0, afRecentPrjs, 1, MAX_RECENT_PRJ-1);
        afRecentPrjs[0] = f;
    }

    /**
     * Returns the Look&Feel class name.
     *
     * @return  the Look&Feel class name
     *
     * @see #setLookAndFeelClassName
     */
    public final String getLookAndFeelClassName() { return sLookAndFeel; }

    /**
     * Set the Look&Feel class name.
     *
     * @param s  a string containing the Look&Feel class name.
     *
     * @see #getLookAndFeelClassName
     */
    public final void setLookAndFeelClassName(String s) { sLookAndFeel = s; }

    /**
     * Returns the path to the default template library file in the temporary
     * folder.
     *
     * @return  the pathname denoting the temporary default template library
     *          file
     *
     * @see #copyGlobalDefaultTemplateFile
     */
    public String getGlobalDefaultTemplateFile() {
        if (fGlobalDefaultTemplateFile == null) {
            try {
                fGlobalDefaultTemplateFile = File.createTempFile(
                    "iPadGlobalDefaultTemplateFile", ".pad",
                    getTempPath());
                copyGlobalDefaultTemplateFile(fGlobalDefaultTemplateFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return fGlobalDefaultTemplateFile.getPath();
    }

    /**
     * Copies the default template libraray file from the .jar file to the given
     * file.
     *
     * @param f  a <code>File</code> specifying the copy target.
     * @exception  IOException if an I/O error occurs.
     *
     * @see #getGlobalDefaultTemplateFile
     */
    public void copyGlobalDefaultTemplateFile(File f) throws IOException {
        ClassLoader cl = getClass().getClassLoader();
        InputStream in = cl.getResourceAsStream("Files/DefaultTemplateLib.pad");
        FileOutputStream fout = new FileOutputStream(f);
        byte[] buffer = new byte[4096];
        int len = 0;
        while ((len = in.read(buffer)) != -1) {
          fout.write(buffer, 0, len);
        }

        fout.close();
        in.close();
    }

    /**
     * Creates and returns a temporary file that can be used for export. It will
     * be deleted when iPad exists.
     *
     * @return  the file denoting the temporary export file
     * @exception  IOException if an I/O error occurs.
     */
    public File createTempExportFile() throws IOException {
        File f = File.createTempFile("iPadExport", ".csv", getTempPath());
        lTempFileList.add(f);
        return f;
    }

    /**
     * Deletes all a temporary file created previously by
     * <code>getGlobalDefaultTemplateFile</code> or
     * <code>createTempExportFile</code>.
     */
    public boolean deleteTempFiles() {
        boolean bSuccess = true;

        if (fGlobalDefaultTemplateFile != null) {
            if (fGlobalDefaultTemplateFile.delete())
                fGlobalDefaultTemplateFile = null;
            else
                bSuccess = false;
        }

        ListIterator iter = lTempFileList.listIterator();
        while (iter.hasNext()) {
            File f = (File)iter.next();
            if (f.delete() || !f.exists())
                iter.remove();
            else
                bSuccess = false;
        }

        return bSuccess;
    }

    //get methods
    public File[] getRecentPrjs() { return afRecentPrjs; }
    public LinkedList getTempFileList(){ return lTempFileList; }

    //set methods
    public void setRecentPrjs( File[] af ) { afRecentPrjs = (File[]) af.clone(); }
    public void setTempFileList( LinkedList l){ lTempFileList = (LinkedList) l.clone(); }
}